package com.huike.springboot1.demos.controller;

import com.github.pagehelper.PageInfo;
import com.huike.springboot1.demos.pojo.Product;
import com.huike.springboot1.demos.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.UUID;

@Controller
public class AdminProductController {
    @Autowired
    ProductService productService;

    //    删除方法二：使用ajax方式
    //    常用项目访问路径
    @GetMapping("/admin/productTable1")
//    控制层通过访问项目路径，可以将数据库中的数据以products为参数名传递到productList页面
//    Model：是SpringMVC中的"M“：模型(数据),前后端交互时的数据模型,后端向前端传递数据使用model    "V"：视图(页面)
    //    法二：也可以用modelAndViewL类型

    public ModelAndView getProductList1(ModelAndView modelAndView, @AuthenticationPrincipal User user) {
//        使用注解参数注入，登录成功后，用户信息会被自动存入 SecurityContextHolder，
//        你可以通过 Spring Security 提供的 API 在任何页面或控制器中获取当前登录的 User对象，这个User是security内置对象，不是自定义对象
//        model将数据作为products参数传递productList(视图)，括号内的内容是，参数名,参数值
        modelAndView.addObject("products", productService.getProductList());

        modelAndView.addObject("currentUser", user.getUsername());
        modelAndView.setViewName("admin/productTable1");
        return modelAndView;
    }

    @DeleteMapping("/admin/deleteProduct1")
    @ResponseBody //把数据转为json格式，返回json格式数据
    public String deleteProduct1(Long id, Model model) {
//        增删改一条数据操作，不需要返回类型，在数据库中增删改一条数据就是返回int的数字类型的行数，没有返回数据类型的意义
        productService.deleteProductById(id);
        //TODO
//        model将数据作为products参数传递productList(视图)，括号内的内容是，参数名,参数值
        model.addAttribute("products", productService.getProductList());
//        return "admin/productTable1";//返回一个在templates路径下的页面名称，productList是templates里的文件
        // 传递到页面的数据product可以通过美元符号$进行获取，在全局配置文件中设置页面文件路径
        return "OK";
    }



    //    打开表单页（新增/修改）
    @GetMapping("/admin/productTabList1")
    public String tabListProduct(@RequestParam(required = false) Long id,
                                 Model model) {
//        @RequestParam(required = false),可以有，也可以没有，因为新增功能的id本来不存在，修改功能的则要根据id来获取数据再进行修改
        if (id != null) {                 // 修改
            Product product = productService.selectProductById(id);
            model.addAttribute("product", product);
        } else {                          // 新增
            model.addAttribute("product", new Product());
        }
        return "admin/productTabList1";    // html页面
    }

    @PostMapping("/admin/postProductList1")
    @ResponseBody
    //    @ResponseBody 使用后返回值为字符串
    public String postProduct(@RequestBody Product product) {
//     @RequestBody   请求体里那段 JSON/XML自动反序列化成一个 Product 对象，@RequestBody	接收请求体中的 JSON/XML 数据
        if (product.getId() == null) {
            productService.insertProduct(product);   // 新增
//            System.out.println("收到商品：" + product);//测试是否能接收到数据
            return "PostOK";
        } else {
            productService.updateProductById(product);   // 修改
//            System.out.println("收到商品：" + product);//测试是否能接收到数据
            return "PutOK";
        }
    }


    //    多条件查询
//    @PostMapping("/searchProduct1")
//    @ResponseBody 使用后返回值为字符串
    //    如果前后端(视图层和控制层)的name属性的属性值参数名一致，如name="productName"，则可直接写： 类型 变量名 如： String name；
//    如果不一致则：要使用@RequestParam设置请求参数名称：@RequestParam(value = "旧变量名"  required = false) 类型 新变量名 ，类型用包装类型
//    如： @RequestParam(value = "minAmount", required = false) Integer min
//    类型要对应，否则会报错，Amount要用Double或BigDecimal，否则类型不对应会报错400
//    @RequestParam(...,required=true)非必填；@RequestParam(...,required=false)必填；@RequestParam(...,defaultValue="0")必填

//单独多条件查询，不包含分页查询
//    public String searchProduct(Model model, String productName, @RequestParam(value = "minAmount", required = false) BigDecimal min, BigDecimal maxAmount, String productCategory) {
////        测试是否能拿到数据
//        List<Product> list= productService.searchProduct(productName,min,maxAmount,productCategory);
//        System.out.println(productName + "," + min + "," + maxAmount + "," + productCategory);
//        model.addAttribute("products", list);
////        通过替换部分页面内容来回显数据，不刷新页面，只是通过把数据返回到select页面中回应数据进行刷新
//        return "basic/select :: prodList";
//    }

    @GetMapping ("/admin/searchProduct1")
//    设置默认起始页码和页数
    public String searchProduct1(Model model, String productName, @RequestParam(value = "minAmount", required = false) BigDecimal min, BigDecimal maxAmount, String productCategory,
                                 @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "9") int pageSize){
        PageInfo<Product> pageInfo = productService.searchProduct1(productName, min, maxAmount, productCategory, pageNum, pageSize);
//        System.out.println("pageInfo = " + pageInfo);
        model.addAttribute("products", pageInfo.getList());//纯数据
        model.addAttribute("page", pageInfo);//纯页码
//        通过替换部分页面内容来回显数据，不刷新页面，只是通过把数据返回到select页面中回应数据进行刷新
        return "basic/select :: prodList";
    }


    // 向文件上传页面跳转
    @GetMapping("/admin/toUpload")
    public String toUpload(){
        return "admin/upload";
    }
    // 文件上传管理
    @PostMapping("/admin/uploadFile")
    public String uploadFile(MultipartFile[] fileUpload, Model model) {
        // 默认文件上传成功，并返回状态信息
        model.addAttribute("uploadStatus", "上传成功！");
        for (MultipartFile file : fileUpload) {
            // 获取文件名以及后缀名
            String fileName = file.getOriginalFilename();
            // 重新生成文件名（根据具体情况生成对应文件名）
            fileName = UUID.randomUUID()+"_"+fileName;
            // 指定上传文件本地存储目录，不存在则需要提前创建
            String dirPath = "G:\\Java\\project\\Springboot1\\src\\main\\resources\\upload\\";
            File filePath = new File(dirPath);
            if(!filePath.exists()){
                filePath.mkdirs();
            }
            try {
                file.transferTo(new File(dirPath+fileName));
            } catch (Exception e) {
                e.printStackTrace();
                // 上传失败，返回失败信息
                model.addAttribute("uploadStatus","上传失败： "+e.getMessage());
            }
        }
        // 携带上传状态信息回调到文件上传页面
        return "admin/upload";
    }

    // 向文件下载页面跳转
    @GetMapping("/admin/download")
    public String toDownload(){
        return "admin/download";
    }

    @PostMapping("/admin/downLoadFile")
    public String uploadFile(@RequestParam("fileUpload") MultipartFile file,
                             Model model) throws IOException {
        if (!file.isEmpty()) {
            String fileName = file.getOriginalFilename();
            // 1. 获取系统临时目录
            String tmpDir = System.getProperty("java.io.tmpdir");
            // 2. 保存
            File target = new File(tmpDir, fileName);
            file.transferTo(target);
            // 3. 回传
            model.addAttribute("fileName", fileName);
        }
        return "admin/download";
    }


//     文件下载接口
//     从系统临时目录中读取指定文件名并返回给浏览器
//     支持中文文件名，使用 UTF-8 编码防止乱码
//     @param request  用于后续扩展（如区分浏览器）
//     @param filename 前端传入的文件名（与上传时保持一致）
//     @return 200-下载文件 / 404-文件不存在
//     @throws IOException 读取失败时抛出
    @GetMapping("/admin/toDownload")
    public ResponseEntity<byte[]> fileDownload(
            HttpServletRequest request,
            @RequestParam String filename) throws IOException {
        // 1. 定位系统临时目录
        String tmpDir = System.getProperty("java.io.tmpdir");
        File file = new File(tmpDir, filename);
        // 2. 文件不存在直接返回 404
        if (!file.exists()) {
            return ResponseEntity.notFound().build();
        }// 3. 对文件名进行 URL 编码，解决中文空格等特殊字符问题
        HttpHeaders headers = new HttpHeaders();
        String encoded = URLEncoder.encode(filename, "UTF-8")
                .replaceAll("\\+", "%20");
//        headers.setContentDispositionFormData("attachment", "UTF-8''" + encoded);
//        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//       构造响应头： 前者管“下载+文件名”，后者管“浏览器别预览，直接下，也可以在return中设置
        // 4. 构造响应头，指示浏览器以附件形式下载
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename*=UTF-8''" + encoded)
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(Files.readAllBytes(file.toPath()));
    }
}
